package com.fyb.basic.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 公平锁/非公平锁：ReentrantLock 和  Synchronize 默认都是非公平锁
 * 可重入锁（递归锁）:避免死锁  一个线程获得外层函数的锁之后 内存的递归函数仍然可以获得到对应锁 而且是同一把锁
 * 自旋锁（Unsafe类）：尝试获取锁的线程不会被阻塞 而是采用循环的方式去尝试获取锁 减少线程上下文切换的销毁 就是浪费一点CPU资源
 * 读(共享锁)写/(独占锁)锁/(互斥锁)：为了增加并发量
 *  读读能共存
 *  读写不能共存
 *  写写不能共存
 * 线程操作资源类
 */
public class ReentrantLockDemo {

    public static void main(String[] args) {
//        testSync();
        testReentrantLock();
    }

    private static void testReentrantLock() {
        Phone phone1 = new Phone();
        new Thread(phone1, "t3").start();
        new Thread(phone1, "t4").start();
    }

    /**
     * 启动两个线程 测试 Sync可重入锁（递归锁）
     */
    private static void testSync() {
        Phone phone = new Phone();
        new Thread(new Runnable() {
            @Override
            public void run() {
                phone.sendSMS();
            }
        }, "t1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                phone.sendSMS();
            }
        }, "t2").start();
    }
}

/**
 * 资源类 为了synchronize 和 lock 的可重入锁
 */
class Phone implements Runnable {
    public synchronized void sendSMS() {
        System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS");
        sendMail();
    }

    public synchronized void sendMail() {
        System.out.println(Thread.currentThread().getName() + "\t invoked sendMail");

    }

    /**
     * 测试 ReentrantLock 可重入锁
     */
    @Override
    public void run() {
        get();
    }

    Lock lock = new ReentrantLock(); // 默认是非公平的

    public void get() {
        try {
            // Lock 必须和unLock 配对使用
            lock.lock();
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "\t invoked get");
            get1();
        } finally {
            lock.unlock();
            lock.unlock();
        }
    }

    public void get1() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "\t invoked get1");
        } finally {
            lock.unlock();
        }
    }


}